﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using WhittleList.Tests.Mocks;
using WhittleList.Tests.Helpers;
using WhittleList.Bll;
using WhittleList.Bll.Exceptions;

namespace WhittleList.Tests.Repositories
{
    [TestClass]
    public class SiteRepositoryTest
    {
        List<FakeUser> userData;
        List<FakeSite> sitesData;
        List<FakeProject> projectsData;
        List<FakeUserProjectRole> userProjectRolesData;
        List<FakeSection> sectionsData;
        List<FakeItem> itemsData;
        FakeDBContext dbContext;
        SiteRepository repository;

        [TestInitialize]
        public void Init()
        {
            userData = FakeUser.GetList(3);

            sitesData = FakeSite.GetList(10);
            projectsData = FakeProject.GetList(10);
            userProjectRolesData = FakeUserProjectRole.GetList(9);
            sectionsData = FakeSection.GetList(8);
            itemsData = FakeItem.GetList(12);

            sitesData[0].SetProjects(projectsData.Take(6).ToList());
            sitesData[1].SetProjects(projectsData.Skip(6).Take(2).ToList());
            sitesData[2].SetProjects(projectsData.Skip(8).ToList());

            //set user project roles so each of first 3 project have three roles one for each user
            userProjectRolesData.Take(3).ToList().ForEach(x => x.SetUser(userData[0]));
            userProjectRolesData.Take(3).ToList().ForEach(x => x.SetProject(projectsData[userProjectRolesData.IndexOf(x) % 3]));
            userProjectRolesData.Skip(3).Take(3).ToList().ForEach(x => x.SetUser(userData[1]));
            userProjectRolesData.Skip(3).Take(3).ToList().ForEach(x => x.SetProject(projectsData[userProjectRolesData.IndexOf(x) % 3]));
            userProjectRolesData.Skip(6).Take(3).ToList().ForEach(x => x.SetUser(userData[2]));
            userProjectRolesData.Skip(6).Take(3).ToList().ForEach(x => x.SetProject(projectsData[userProjectRolesData.IndexOf(x) % 3]));

            projectsData[0].SetSections(sectionsData.Take(6).ToList());
            projectsData[1].SetSections(sectionsData.Skip(6).ToList());


            sectionsData[0].SetItems(itemsData.Take(10).ToList());
            sectionsData[1].SetItems(itemsData.Skip(10).ToList());

            dbContext = new FakeDBContext();
            dbContext.Setup(x => x.Sites).Returns(sitesData.Convert());
            dbContext.Setup(x => x.Projects).Returns(projectsData.Convert());
            dbContext.Setup(x => x.UserProjectRoles).Returns(userProjectRolesData.Convert());
            dbContext.Setup(x => x.Users).Returns(userData.Convert());
            dbContext.Setup(x => x.Sections).Returns(sectionsData.Convert());
            dbContext.Setup(x => x.Items).Returns(itemsData.Convert());

            repository = new SiteRepository(dbContext.Object);
        }

        [TestMethod]
        public void Site_GetById_InvvalidId_ReturnsNull()
        {
            Assert.IsNull(repository.Site_GetById(int.MaxValue));
        }

        [TestMethod]
        public void Site_GetById_ValidId_ReturnsCorrectSite()
        {
            var tmp = repository.Site_GetById(sitesData[4].Object.Id);
            Assert.IsNotNull(tmp);
            Assert.AreEqual(sitesData[4].Object, tmp);
        }

        [TestMethod]
        public void Site_GetByUrl_InvalidUrl_ReturnsNull()
        {
            var tmp = repository.Site_GetByUrl("rubbish");
            Assert.IsNull(tmp);
        }

        [TestMethod]
        public void Site_GetByUrl_ValidUrl_ReturnsCorrectItem()
        {
            var tmp = repository.Site_GetByUrl(sitesData[2].Object.Url);
            Assert.IsNotNull(tmp);
            Assert.AreEqual(sitesData[2].Object, tmp);
        }

        [TestMethod]
        public void Site_GetAllByOwnerId_InvalidId_ReturnsEmptyList()
        {
            var tmp = repository.Site_GetAllByOwnerId(int.MaxValue).ToList();
            Assert.IsNotNull(tmp);
            Assert.AreEqual(0, tmp.Count);
        }

        [TestMethod]
        public void Site_GetAllByOwnerId_ValidId_ReturnsPopulatedList()
        {
            sitesData.Skip(1).Take(4).ToList().ForEach(x => x.Setup(y => y.OwnerId).Returns(5));
            var tmp = repository.Site_GetAllByOwnerId(5).ToList();
            Assert.IsNotNull(tmp);
            Assert.AreEqual(4, tmp.Count);
            Assert.AreEqual(sitesData[1].Object, tmp[0]);
            Assert.AreEqual(sitesData[2].Object, tmp[1]);
            Assert.AreEqual(sitesData[3].Object, tmp[2]);
            Assert.AreEqual(sitesData[4].Object, tmp[3]);
        }

        [TestMethod]
        public void Site_GetAllByOwner_ValidOwner_ReturnsPopulatedList()
        {
            var tmpUser = new FakeUser(5, null, null, null, null);
            sitesData.Skip(4).Take(2).ToList().ForEach(x => x.Setup(y => y.OwnerId).Returns(5));
            var tmp = repository.Site_GetAllByOwner(tmpUser.Object).ToList();
            Assert.IsNotNull(tmp);
            Assert.AreEqual(2, tmp.Count);
            Assert.AreEqual(sitesData[4].Object, tmp[0]);
            Assert.AreEqual(sitesData[5].Object, tmp[1]);
        }

        [TestMethod]
        public void Site_GetAllByOwner_InValidOwner_ReturnsPopulatedList()
        {
            var tmpUser = new FakeUser(int.MaxValue, null, null, null, null);
            var tmp = repository.Site_GetAllByOwner(tmpUser.Object).ToList();
            Assert.IsNotNull(tmp);
            Assert.AreEqual(0, tmp.Count);
        }

        [TestMethod]
        [ExpectedException(typeof(DuplicateUrlException))]
        public void Site_InsertDuplicateUrl_Throws_Exception()
        {
            var tmpUser = new FakeUser(1, null, null, null, null);
            var site = new Site();
            site.Url = sitesData[2].Object.Url;

            dbContext.Setup(x => x.GetInserts<Site>()).Returns(new[] { site });

            repository.Insert(site);
            repository.SaveChanges(tmpUser.Object);
        }

        [TestMethod]
        [ExpectedException(typeof(DuplicateNameException))]
        public void Site_InsertDuplicateName_Throws_Exception()
        {
            var tmpUser = new FakeUser(1, null, null, null, null);
            var site = new Site();
            site.Name = sitesData[2].Object.Name;

            dbContext.Setup(x => x.GetInserts<Site>()).Returns(new[] { site });

            repository.Insert(site);
            repository.SaveChanges(tmpUser.Object);
        }

        [TestMethod]
        [ExpectedException(typeof(DuplicateUrlException))]
        public void Site_UpdateDuplicateUrl_Throws_Exception()
        {
            var tmpUser = new FakeUser(1, null, null, null, null);
            sitesData[4].Object.Url = sitesData[2].Object.Url;

            dbContext.Setup(x => x.GetUpdates<Site>()).Returns(sitesData.Convert());

            repository.SaveChanges(tmpUser.Object);
        }

        [TestMethod]
        [ExpectedException(typeof(DuplicateNameException))]
        public void Site_UpdateDuplicateName_Throws_Exception()
        {
            var tmpUser = new FakeUser(1, null, null, null, null);
            sitesData[4].Object.Name = sitesData[2].Object.Name;

            dbContext.Setup(x => x.GetUpdates<Site>()).Returns(sitesData.Convert());

            repository.SaveChanges(tmpUser.Object);
        }

        [TestMethod]
        public void Site_Insert_SetCreatedAndUpdated()
        {
            var tmp = DateTime.Now;
            var tmpUser = new FakeUser(1, null, null, null, null);

            var site = new Site();

            dbContext.Setup(x => x.GetInserts<Site>()).Returns(new[] { site });

            site.Name = "Insert Test";
            site.Url = "insert";
            site.OwnerId = tmpUser.Object.Id;

            repository.Insert(site);

            repository.SaveChanges(tmpUser.Object);

            Assert.AreEqual(tmpUser.Object.Id, site.LastUpdatedById);
            Assert.AreEqual(tmpUser.Object.Id, site.CreatedById);

            Assert.IsTrue(tmp <= site.LastUpdated);
            Assert.IsTrue(tmp <= site.Created);

            Assert.IsTrue(DateTime.Now >= site.LastUpdated);
            Assert.IsTrue(DateTime.Now >= site.Created);

        }

        [TestMethod]
        public void Site_Update_SetsUpdated()
        {
            var tmp = DateTime.Now;
            var tmpUser = new FakeUser(1, null, null, null, null);

            var site = new Site();

            dbContext.Setup(x => x.GetUpdates<Site>()).Returns(new[] { site });

            site.Name = "Update Test";
            site.Url = "update";
            site.OwnerId = tmpUser.Object.Id;

            repository.Insert(site);

            repository.SaveChanges(tmpUser.Object);

            Assert.AreEqual(tmpUser.Object.Id, site.LastUpdatedById);
            Assert.AreEqual(0, site.CreatedById);

            Assert.IsTrue(tmp <= site.LastUpdated);
            Assert.IsTrue(DateTime.Now >= site.LastUpdated);
            Assert.AreEqual(DateTime.MinValue, site.Created);

        }

        [TestMethod]
        public void Site_SaveChanges_CallsSaveChangesinDBContext()
        {
            var tmpUser = new FakeUser(1, null, null, null, null);

            repository.SaveChanges(tmpUser.Object);

            dbContext.Verify(x => x.GetInserts<Site>(), Moq.Times.Once());
            dbContext.Verify(x => x.GetUpdates<Site>(), Moq.Times.Once());
            dbContext.Verify(x => x.SubmitChanges(), Moq.Times.Once());
        }

        [TestMethod]
        public void Project_GetById_InvalidId_ReturnsNull()
        {
            Assert.IsNull(repository.Project_GetById(int.MaxValue));
        }

        [TestMethod]
        public void Project_GetById_ValidId_ReturnsCorrectItem()
        {
            var tmp = repository.Project_GetById(projectsData[4].Object.Id);
            Assert.IsNotNull(tmp);
            Assert.AreEqual(projectsData[4].Object, tmp);
        }

        [TestMethod]
        public void Project_GetAllBySiteId_InValidId_ReturnsEmptyList()
        {
            var tmp = repository.Project_GetAllBySiteId(int.MaxValue);
            Assert.IsNotNull(tmp);
            Assert.AreEqual(0, tmp.Count());
        }

        [TestMethod]
        public void Project_GetAllBySiteId_ValidId_ReturnsCorrectList()
        {
            var tmp = repository.Project_GetAllBySiteId(sitesData[1].Object.Id);
            Assert.IsNotNull(tmp);
            Assert.AreEqual(sitesData[1].Object.Projects.Count, tmp.Count());
        }

        [TestMethod]
        public void Project_GetAllBySite_ReturnsCorrectList()
        {
            var tmp = repository.Project_GetAllBySite(sitesData[2].Object);
            Assert.IsNotNull(tmp);
            Assert.AreEqual(sitesData[2].Object.Projects.Count, tmp.Count());
        }

        [TestMethod]
        public void Project_GetAllByUserId_InvalidUserId_ReturnsEmptyList()
        {
            var tmp = repository.Project_GetAllByUserId(int.MaxValue, null);
            Assert.IsNotNull(tmp);
            Assert.AreEqual(0, tmp.Count());
        }

        [TestMethod]
        public void Project_GetAllByUserId_ValidUserId_ReturnsCorrect()
        {
            var tmp = repository.Project_GetAllByUserId(userData[0].Object.Id, null);
            Assert.IsNotNull(tmp);
            Assert.AreEqual(userProjectRolesData.Where(x => x.Object.UserId == userData[0].Object.Id).Count(), tmp.Count());
            Assert.AreNotEqual(0, tmp.Count());
        }

        [TestMethod]
        public void Project_GetAllByUserId_ValidUserId_NotApproved_ReturnsCorrect()
        {
            int userId = userData[0].Object.Id;
            userProjectRolesData.Where(x => x.Object.UserId == userId).First().Object.Approved = true;
            var tmp = repository.Project_GetAllByUserId(userId, false);
            Assert.IsNotNull(tmp);
            Assert.AreEqual(userProjectRolesData.Where(x => x.Object.UserId == userData[0].Object.Id && !x.Object.Approved).Count(), tmp.Count());
            //check there was some data and not an empty test
            Assert.AreNotEqual(0, tmp.Count());
        }

        [TestMethod]
        public void Project_GetAllByUserId_ValidUserId_Approved_ReturnsCorrect()
        {
            int userId = userData[0].Object.Id;
            userProjectRolesData.Where(x => x.Object.UserId == userId).Take(4).ToList().ForEach(x => x.Object.Approved = true);
            var tmp = repository.Project_GetAllByUserId(userId, true);
            Assert.IsNotNull(tmp);
            Assert.AreEqual(userProjectRolesData.Where(x => x.Object.UserId == userData[0].Object.Id && x.Object.Approved).Count(), tmp.Count());
            //check there was some data and not an empty test
            Assert.AreNotEqual(0, tmp.Count());
        }

        [TestMethod]
        public void Project_GetAllUsersByProjectId_InvalidId_ReturnsEmptyList()
        {
            var tmp = repository.Project_GetAllUsersByProjectId(int.MaxValue, null);
            Assert.IsNotNull(tmp);
            Assert.AreEqual(0, tmp.Count());
        }

        [TestMethod]
        public void Project_GetAllUsersByProjectId_ValidId_ReturnsCorrectList()
        {
            var tmp = repository.Project_GetAllUsersByProjectId(projectsData[0].Object.Id, null);
            Assert.IsNotNull(tmp);
            Assert.AreEqual(userProjectRolesData.Where(x => x.Object.ProjectId == projectsData[0].Object.Id).Count(), tmp.Count());
        }

        [TestMethod]
        public void Project_GetAllUsersByProjectId_ValidId_NotApproved_ReturnsCorrectList()
        {
            userProjectRolesData.Where(x => x.Object.ProjectId == projectsData[0].Object.Id).Take(1).ToList().ForEach(x => x.Object.Approved = true);
            var tmp = repository.Project_GetAllUsersByProjectId(projectsData[0].Object.Id, false);
            Assert.IsNotNull(tmp);
            Assert.AreEqual(2, tmp.Count());
        }

        [TestMethod]
        public void Project_GetAllUsersByProjectId_ValidId_Approved_ReturnsCorrectList()
        {
            userProjectRolesData.Where(x => x.Object.ProjectId == projectsData[0].Object.Id).Take(2).ToList().ForEach(x => x.Object.Approved = true);
            var tmp = repository.Project_GetAllUsersByProjectId(projectsData[0].Object.Id, true);
            Assert.IsNotNull(tmp);
            Assert.AreEqual(2, tmp.Count());
        }

        [TestMethod]
        public void Section_GetById_InvalidId_ReturnsNull()
        {
            var tmp = repository.Section_GetById(int.MaxValue);
            Assert.IsNull(tmp);
        }

        [TestMethod]
        public void Section_GetById_ValidId_ReturnsCorrectItem()
        {
            var tmp = repository.Section_GetById(sectionsData[0].Object.Id);
            Assert.IsNotNull(tmp);
            Assert.AreEqual(sectionsData[0].Object, tmp);
        }

        [TestMethod]
        public void Section_GetAllByProjectId_InValidId_ReturnsEmptyList()
        {
            var tmp = repository.Section_GetAllByProjectId(int.MaxValue);
            Assert.IsNotNull(tmp);
            Assert.AreEqual(0, tmp.Count());
        }

        [TestMethod]
        public void Section_GetAllByProjectId_ValidId_ReturnsCorrectList()
        {
            var tmp = repository.Section_GetAllByProjectId(projectsData[0].Object.Id);
            Assert.IsNotNull(tmp);
            Assert.AreEqual(sectionsData.Where(x => x.Object.ProjectId == projectsData[0].Object.Id).Count(), tmp.Count());
        }

        [TestMethod]
        public void Item_GetById_InValidId_ReturnsNull()
        {
            var tmp = repository.Item_GetById(int.MaxValue);
            Assert.IsNull(tmp);
        }

        [TestMethod]
        public void Item_GetById_ValidId_ReturnsCorrectItem()
        {
            var tmp = repository.Item_GetById(itemsData[5].Object.Id);
            Assert.IsNotNull(tmp);
            Assert.AreEqual(itemsData[5].Object, tmp);
        }

        [TestMethod]
        public void Item_GetByAllBySectionId_InValidId_ReturnsEmptyList()
        {
            var tmp = repository.Item_GetByAllBySectionId(int.MaxValue);
            Assert.IsNotNull(tmp);
            Assert.AreEqual(0, tmp.Count());
        }

        [TestMethod]
        public void Item_GetByAllBySectionId_ValidId_ReturnsPopulatedList()
        {
            var tmp = repository.Item_GetByAllBySectionId(sectionsData[0].Object.Id);
            Assert.IsNotNull(tmp);
            Assert.AreEqual(sectionsData[0].Object.Items.Count(), tmp.Count());
        }
    }
}
